﻿<template>
    <div class="player-box-grid" v-draggable-resizable="{
        storageKey: 'player-box',
        controlsPosition: 'top',
        hideHintText: '玩家框',
        minScale: 0.5,
        maxScale: 2
    }" @mouseenter="showControls = true" @mouseleave="showControls = false">

        <!-- 演示模式切换按钮 -->
        <div v-if="showControls" class="demo-toggle" @click="toggleDemo">
            <span>{{ isInternalDemo ? '退出演示' : '演示模式' }}</span>
        </div>

        <!-- 蓝方一列 -->
        <div class="column red">
            <div class="scroll-container">
                <div class="players-wrapper blue-wrapper" :style="{ 
                    transform: `translateY(-${blueScrollOffset}px)`,
                    height: blueNeedsScroll ? `${blueTotalHeight}px` : 'auto' }">
                    <div v-for="player in virtualBluePlayers" 
                        :key="String(player.player_id)" 
                        class="player-cell virtual-player" 
                        :style="{
                            ...cellStyle, 
                            background: 'rgba(232, 120, 22, 0.5)',
                            position: blueNeedsScroll ? 'absolute' : 'relative',
                            top: blueNeedsScroll ? `${player.offsetTop}px` : 'auto',
                            width: '100%' }">
                        <img class="avatar" :src="player.avatar" :style="avatarStyle" @error="handleAvatarError($event, player.avatar)" />
                        <img class="avatar-frame" :src="getAvatarFrame(getPlayerTotalPoints(player))" />
                        <img class="rank-box" :src="getNumBox(getPlayerTotalPoints(player))"/>
                        <span class="rank-text">{{ getRank(getPlayerTotalPoints(player)) }}</span>
                        <div class="info">
                            <div class="player-name" :style="nameStyle" :title="player.player_name">{{ player.player_name }}</div>
                            <div class="stats-row">
                                <div class="stat" :style="statStyle">
                                    <span class="label">实时积分</span>
                                    <span class="value">{{ getScoreValue(player) }}</span>
                                </div>
                                <div class="stat" :style="statStyle">
                                    <span class="label">军功</span>
                                    <span
                                        v-if="shouldShowMilitaryFloat(player)"
                                        class="military-float"
                                        :class="getMilitaryFloatClass(player)"
                                        :key="getMilitaryFloatKey(player)"
                                    >
                                        {{ getMilitaryFloatText(player) }}
                                    </span>
                                    <span class="value">{{ getMilitaryPoints(player) }}</span>
                                </div>
                            </div>
                            <div class="stats-row">
                                <div class="badge" :style="{ backgroundImage:getBadgeStyle(getPlayerTotalPoints(player) || player.badge_style) }">
                                    <img :src="getBadge(getPlayerTotalPoints(player)) || player.img_url || defaultImageUrl" alt="badge" />
                                    <span>{{ `月${player.rank || '-'}  周${player.week_rank || '-'}` }}</span>
                                </div>
                                <div class="stat" :style="statStyle">
                                    <span class="label">兵力</span><span class="value">{{ player.units || 0 }}</span>
                                </div>
                            </div>
                            <div class="progress-row">
                                <div class="progress-bar-container" :style="{ width: progressBarWidth }">
                                    <div class="progress-bar-fill" :style="{ width: `${player.progress || 0}%` }"></div>
                                </div>
                                <img :src="getLikeGifSrc(player)"
                                    class="progress-like-gif"
                                    :style="likeGifStyle"
                                    alt="like animation" />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 红方一列 -->
        <div class="column bule">
            <div class="scroll-container">
                <div class="players-wrapper red-wrapper" :style="{ 
                    transform: `translateY(-${redScrollOffset}px)`,
                    height: redNeedsScroll ? `${redTotalHeight}px` : 'auto' }">
                    <div v-for="player in virtualRedPlayers" 
                        :key="String(player.player_id)" 
                        class="player-cell virtual-player" 
                        :style="{
                            ...cellStyle, 
                            background: 'rgba(25, 199, 136, 0.5)',
                            position: redNeedsScroll ? 'absolute' : 'relative',
                            top: redNeedsScroll ? `${player.offsetTop}px` : 'auto',
                            width: '100%' }">
                        <img class="avatar" :src="player.avatar" :style="avatarStyle" @error="handleAvatarError($event, player.avatar)" />
                        <img class="avatar-frame" :src="getAvatarFrame(getPlayerTotalPoints(player))" />
                        <img class="rank-box" :src="getNumBox(getPlayerTotalPoints(player))"/>
                        <span class="rank-text">{{ getRank(getPlayerTotalPoints(player)) }}</span>
                        <div class="info">
                            <div class="player-name" :style="nameStyle" :title="player.player_name">{{ player.player_name }}</div>
                            <div class="stats-row">
                                <div class="stat" :style="statStyle">
                                    <span class="label">实时积分</span>
                                    <span class="value">{{ getScoreValue(player) }}</span>
                                </div>
                                <div class="stat" :style="statStyle">
                                    <span class="label">军功</span>
                                    <span
                                        v-if="shouldShowMilitaryFloat(player)"
                                        class="military-float"
                                        :class="getMilitaryFloatClass(player)"
                                        :key="getMilitaryFloatKey(player)"
                                    >
                                        {{ getMilitaryFloatText(player) }}
                                    </span>
                                    <span class="value">{{ getMilitaryPoints(player) }}</span>
                                </div>
                            </div>
                            <div class="stats-row">
                                <div class="badge" :style="{ backgroundImage: getBadgeStyle(getPlayerTotalPoints(player)) || player.badge_style }">
                                    <img :src="getBadge(getPlayerTotalPoints(player)) || player.img_url || defaultImageUrl" alt="badge" />
                                    <span>{{ `月${player.rank || '-'}  周${player.week_rank || '-'}` }}</span>
                                </div>
                                <div class="stat" :style="statStyle">
                                    <span class="label">兵力</span><span class="value">{{ player.units || 0 }}</span>
                                </div>
                            </div>
                            <div class="progress-row">
                                <div class="progress-bar-container" :style="{ width: progressBarWidth }">
                                    <div class="progress-bar-fill" :style="{ width: `${player.progress || 0}%` }"></div>
                                </div>
                                <img :src="getLikeGifSrc(player)"
                                    class="progress-like-gif"
                                    :style="likeGifStyle"
                                    alt="like animation" />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { defineProps, computed, ref, onMounted, onUnmounted, watch, nextTick, type CSSProperties } from 'vue';
import eventBus from 'vue3-eventbus'
import avatar from '@/assets/xinJi/images/leaderboard/avatar.png';
import likeGif from '@/assets/xinJi/images/like.gif';
import { calculatePlayerScores, calculatePlayerScoresAsync } from '@/utils/scoreCalculator';
import defaultImageUrl from '@/assets/xinJi/images/badge/1.png';
import { getRank, getAvatarFrame, getBadge, getBadgeStyle, getNumBox, handleAvatarError } from '@/utils/playerBox';
import getApi from '@/api/modules/get';

// 单个玩家信息类型
interface PlayerInfo {
    player_id: string;
    userid_platform: string;
    progress?: number;
    avatar: string;
    player_name: string;
    score: number | string | null | undefined;
    battle: number;
    units: number;
    kills: number;
    rank?: number;
    week_rank?: number;
    total_players?: number;
    team?: string;
    season_points?: number;
    img_url?: string;
    badge_style?: string;
    points?: number; // 添加points字段用于演示数据
    seasonDataReady?: boolean | null;
}
// 礼物消息类型
interface GiftMessage {
    player_id: string;
    team: string;
    gift_point: number;
    gift_count: number;
    uuid?: string;
}

// Props定义
const props = defineProps<{
    bluePlayers: PlayerInfo[];
    redPlayers: PlayerInfo[];
    scale: number;
    redHpCurrent: number;
    blueHpCurrent: number;
    pointPool: number;
    messages?: GiftMessage[];
    gameId: number;
}>();
// 演示模式相关
const isInternalDemo = ref(false);
const demoBluePlayers = ref<PlayerInfo[]>([
    // 蓝队：等级1-12 (橙色主题)
    { player_id: '1', userid_platform: '1', avatar: avatar, player_name: '等级1', score: 1500, battle: 15, units: 125000, kills: 8, rank: 1, week_rank: 120, total_players: 100, season_points: 50, progress: 0 },           // 等级1: <100
    { player_id: '2', userid_platform: '2', avatar: avatar, player_name: '等级2', score: 1200, battle: 12, units: 98000, kills: 6, rank: 3, week_rank: 13, total_players: 100, season_points: 1000, progress: 60 },         // 等级2: 100-1999
    { player_id: '3', userid_platform: '3', avatar: avatar, player_name: '等级3', score: 980, battle: 10, units: 82000, kills: 5, rank: 5, week_rank: 14, total_players: 100, season_points: 4000, progress: 90 },          // 等级3: 2000-5999
    { player_id: '4', userid_platform: '4', avatar: avatar, player_name: '等级4', score: 850, battle: 8, units: 75000, kills: 4, rank: 8, week_rank: 15, total_players: 100, season_points: 8000, progress: 45 },           // 等级4: 6000-9999
    { player_id: '5', userid_platform: '5', avatar: avatar, player_name: '等级5', score: 720, battle: 7, units: 68000, kills: 3, rank: 12, week_rank: 16, total_players: 100, season_points: 20000, progress: 80 },         // 等级5: 10000-29999
    { player_id: '6', userid_platform: '6', avatar: avatar, player_name: '等级6', score: 650, battle: 6, units: 62000, kills: 2, rank: 15, week_rank: 17, total_players: 100, season_points: 50000, progress: 30 },         // 等级6: 30000-69999
    { player_id: '7', userid_platform: '7', avatar: avatar, player_name: '等级7', score: 580, battle: 5, units: 55000, kills: 1, rank: 18, week_rank: 18, total_players: 100, season_points: 70000, progress: 100 },        // 等级7: 70000-99999
    { player_id: '8', userid_platform: '8', avatar: avatar, player_name: '等级8', score: 520, battle: 4, units: 48000, kills: 0, rank: 20, week_rank: 19, total_players: 100, season_points: 100000, progress: 20 },        // 等级8: 100000-149999
    { player_id: '9', userid_platform: '9', avatar: avatar, player_name: '等级9', score: 520, battle: 4, units: 48000, kills: 0, rank: 20, week_rank: 19, total_players: 100, season_points: 150000, progress: 55 },        // 等级9: 150000-249999
    { player_id: '10', userid_platform: '10', avatar: avatar, player_name: '等级10', score: 520, battle: 4, units: 48000, kills: 0, rank: 20, week_rank: 19, total_players: 100, season_points: 250000, progress: 70 },      // 等级10: 250000-499999
    { player_id: '11', userid_platform: '11', avatar: avatar, player_name: '等级11', score: 520, battle: 4, units: 48000, kills: 0, rank: 20, week_rank: 19, total_players: 100, season_points: 500000, progress: 85 },      // 等级11: 500000-699999
    { player_id: '12', userid_platform: '12', avatar: avatar, player_name: '等级12', score: 520, battle: 4, units: 48000, kills: 0, rank: 20, week_rank: 19, total_players: 100, season_points: 700000, progress: 10 },     // 等级12: 700000-999999
]);
const demoRedPlayers = ref<PlayerInfo[]>([
    // 红队：等级13-25 (绿色主题)
    { player_id: '13', userid_platform: '13', avatar: avatar, player_name: '等级13', score: 1800, battle: 18, units: 1500000, kills: 12, rank: 2, week_rank: 900, total_players: 100, season_points: 1200000, progress: 88 },    // 等级13: 1000000-1499999
    { player_id: '14', userid_platform: '14', avatar: avatar, player_name: '等级14', score: 1350, battle: 14, units: 1120000, kills: 9, rank: 4, week_rank: 91, total_players: 100, season_points: 1750000, progress: 50 },      // 等级14: 1500000-1999999
    { player_id: '15', userid_platform: '15', avatar: avatar, player_name: '等级15', score: 1100, battle: 11, units: 950000, kills: 7, rank: 6, week_rank: 92, total_players: 100, season_points: 2500000, progress: 95 },       // 等级15: 2000000-2999999
    { player_id: '16', userid_platform: '16', avatar: avatar, player_name: '等级16', score: 950, battle: 9, units: 880000, kills: 6, rank: 7, week_rank: 93, total_players: 100, season_points: 3750000, progress: 25 },         // 等级16: 3000000-4499999
    { player_id: '17', userid_platform: '17', avatar: avatar, player_name: '等级17', score: 820, battle: 8, units: 720000, kills: 5, rank: 9, week_rank: 94, total_players: 100, season_points: 5750000, progress: 78 },         // 等级17: 4500000-6999999
    { player_id: '18', userid_platform: '18', avatar: avatar, player_name: '等级18', score: 750, battle: 7, units: 650000, kills: 4, rank: 10, week_rank: 95, total_players: 100, season_points: 8500000, progress: 40 },        // 等级18: 7000000-9999999
    { player_id: '19', userid_platform: '19', avatar: avatar, player_name: '等级19', score: 1800, battle: 18, units: 1500000, kills: 12, rank: 2, week_rank: 900, total_players: 100, season_points: 10000000, progress: 66 },   // 等级19: 10000000-14999999
    { player_id: '20', userid_platform: '20', avatar: avatar, player_name: '等级20', score: 1350, battle: 14, units: 1120000, kills: 9, rank: 4, week_rank: 91, total_players: 100, season_points: 17500000, progress: 33 },      // 等级20: 15000000-24999999
    { player_id: '21', userid_platform: '21', avatar: avatar, player_name: '等级21', score: 1100, battle: 11, units: 950000, kills: 7, rank: 6, week_rank: 92, total_players: 100, season_points: 111689655, progress: 99 },       // 等级21: 25000000-39999999
    { player_id: '22', userid_platform: '22', avatar: avatar, player_name: '等级22', score: 950, battle: 9, units: 880000, kills: 6, rank: 7, week_rank: 93, total_players: 100, season_points: 144763589, progress: 15 },         // 等级22: 40000000-54999999
    { player_id: '23', userid_platform: '23', avatar: avatar, player_name: '等级23', score: 820, battle: 8, units: 720000, kills: 5, rank: 9, week_rank: 94, total_players: 100, season_points: 160000000, progress: 77 },         // 等级23: 55000000-74999999
    { player_id: '24', userid_platform: '24', avatar: avatar, player_name: '等级24', score: 750, battle: 7, units: 650000, kills: 4, rank: 10, week_rank: 95, total_players: 100, season_points: 150000000, progress: 42 },       // 等级24: 75000000-99999999
    { player_id: '25', userid_platform: '25', avatar: avatar, player_name: '等级25', score: 820, battle: 8, units: 720000, kills: 5, rank: 9, week_rank: 94, total_players: 100, season_points: 260678366, progress: 100 },        // 等级25: 100000000-150000000
]);
const demoMessages = ref<GiftMessage[]>([
    { player_id: '1', team: 'blue', gift_point: 100, gift_count: 2, uuid: 'demo-1' },
    { player_id: '2', team: 'blue', gift_point: 50, gift_count: 3, uuid: 'demo-1' },
    { player_id: '3', team: 'blue', gift_point: 200, gift_count: 1, uuid: 'demo-1' },
    { player_id: '21', team: 'red', gift_point: 150, gift_count: 2, uuid: 'demo-1' },
    { player_id: '22', team: 'red', gift_point: 80, gift_count: 4, uuid: 'demo-1' },
    { player_id: '23', team: 'red', gift_point: 120, gift_count: 1, uuid: 'demo-1' },
]);

const demoRedHpCurrent = ref<number>(800);
const demoBlueHpCurrent = ref<number>(600);

// 当前数据计算
const currentBluePlayers = computed(() => isInternalDemo.value ? demoBluePlayers.value : props.bluePlayers);
const currentRedPlayers = computed(() => isInternalDemo.value ? demoRedPlayers.value : props.redPlayers);
const currentMessages = computed(() => isInternalDemo.value ? demoMessages.value : props.messages);

// 军功浮动动画状态
const militaryPointsMap = ref<Record<string, number>>({});
const militaryDiffStates = ref<Record<string, { delta: number; token: number }>>({});
const militaryDiffTimers: Record<string, ReturnType<typeof setTimeout>> = {};
const MILITARY_FLOAT_DURATION = 1200;
const militaryPlayerSnapshots = ref<Record<string, number>>({});

const getPlayerUniqueId = (player: PlayerInfo): string => {
    return String((player as any).userid_platform || player.player_id || '');
};

const ensureMilitaryBaseline = (player: PlayerInfo) => {
    const id = getPlayerUniqueId(player);
    if (!id) {
        return;
    }
    const stored = militaryPointsMap.value[id];
    if (typeof stored === 'number' && !isNaN(stored)) {
        return;
    }
    const fallback = Number((player as any).points ?? 0);
    const baseline = isNaN(fallback) ? 0 : Math.floor(fallback);
    militaryPointsMap.value = { ...militaryPointsMap.value, [id]: baseline };
};

const getMilitaryPoints = (player: PlayerInfo): number => {
    ensureMilitaryBaseline(player);
    const id = getPlayerUniqueId(player);
    const val = militaryPointsMap.value[id];
    if (typeof val === 'number' && !isNaN(val)) {
        return Math.floor(val);
    }
    return 0;
};

const shouldShowMilitaryFloat = (player: PlayerInfo): boolean => {
    const id = getPlayerUniqueId(player);
    return Boolean(militaryDiffStates.value[id]);
};

const getMilitaryFloatKey = (player: PlayerInfo): string => {
    const id = getPlayerUniqueId(player);
    const state = militaryDiffStates.value[id];
    if (!state) {
        return '';
    }
    return `military-float-${id}-${state.token}`;
};

const getMilitaryFloatText = (player: PlayerInfo): string => {
    const id = getPlayerUniqueId(player);
    const state = militaryDiffStates.value[id];
    if (!state) {
        return '';
    }
    const diff = Math.floor(state.delta);
    if (diff === 0) {
        return '';
    }
    return diff > 0 ? `+${diff}` : `${diff}`;
};

const getMilitaryFloatClass = (player: PlayerInfo): string => {
    const id = getPlayerUniqueId(player);
    const state = militaryDiffStates.value[id];
    if (!state) {
        return '';
    }
    if (state.delta > 0) {
        return 'is-positive';
    }
    if (state.delta < 0) {
        return 'is-negative';
    }
    return '';
};

const clearMilitaryDiffState = (id: string) => {
    if (militaryDiffTimers[id]) {
        clearTimeout(militaryDiffTimers[id]);
        delete militaryDiffTimers[id];
    }
    if (militaryDiffStates.value[id]) {
        const { [id]: _, ...rest } = militaryDiffStates.value;
        militaryDiffStates.value = rest;
    }
};

const scheduleMilitaryDiffCleanup = (id: string, token: number) => {
    if (militaryDiffTimers[id]) {
        clearTimeout(militaryDiffTimers[id]);
    }

    const runCleanup = () => {
        const state = militaryDiffStates.value[id];
        if (state && state.token === token) {
            const { [id]: _, ...rest } = militaryDiffStates.value;
            militaryDiffStates.value = rest;
        }
        if (militaryDiffTimers[id]) {
            delete militaryDiffTimers[id];
        }
    };

    if (typeof window === 'undefined') {
        runCleanup();
        return;
    }

    militaryDiffTimers[id] = window.setTimeout(runCleanup, MILITARY_FLOAT_DURATION + 200);
};

const updateMilitaryPointState = (id: string, rawValue: number, player?: PlayerInfo) => {
    if (!id || !Number.isFinite(rawValue)) {
        return;
    }

    const nextValue = Math.floor(rawValue);
    let previousValue = militaryPointsMap.value[id];

    if (typeof previousValue !== 'number' || isNaN(previousValue)) {
        if (player) {
            ensureMilitaryBaseline(player);
        }
        previousValue = militaryPointsMap.value[id] ?? 0;
    }

    previousValue = Math.floor(previousValue);

    if (previousValue === nextValue) {
        return;
    }

    const delta = nextValue - previousValue;
    militaryPointsMap.value = { ...militaryPointsMap.value, [id]: nextValue };

    const token = Date.now();
    militaryDiffStates.value = { ...militaryDiffStates.value, [id]: { delta, token } };
    scheduleMilitaryDiffCleanup(id, token);
};

const syncMilitaryPointsFromPlayer = (player: PlayerInfo) => {
    const id = getPlayerUniqueId(player);
    if (!id) {
        return;
    }
    const rawValue = Number((player as any).points);
    const storedBeforeEnsure = militaryPointsMap.value[id];
    ensureMilitaryBaseline(player);
    const storedAfterEnsure = militaryPointsMap.value[id];
    const normalizedStored = (typeof storedAfterEnsure === 'number' && !isNaN(storedAfterEnsure))
        ? Math.floor(storedAfterEnsure)
        : 0;

    if (!Number.isFinite(rawValue)) {
        if (militaryPlayerSnapshots.value[id] === undefined) {
            militaryPlayerSnapshots.value = { ...militaryPlayerSnapshots.value, [id]: normalizedStored };
        }
        return;
    }

    const normalizedRaw = Math.floor(rawValue);
    const snapshot = militaryPlayerSnapshots.value[id];

    if (snapshot === undefined) {
        const baseline = (typeof storedBeforeEnsure === 'number' && !isNaN(storedBeforeEnsure))
            ? Math.floor(storedBeforeEnsure)
            : normalizedRaw;

        militaryPlayerSnapshots.value = { ...militaryPlayerSnapshots.value, [id]: baseline };

        if (typeof storedBeforeEnsure !== 'number' || isNaN(storedBeforeEnsure)) {
            militaryPointsMap.value = { ...militaryPointsMap.value, [id]: normalizedRaw };
        }
        return;
    }

    if (normalizedRaw === Math.floor(snapshot)) {
        return;
    }

    if (normalizedStored === normalizedRaw) {
        militaryPlayerSnapshots.value = { ...militaryPlayerSnapshots.value, [id]: normalizedRaw };
        return;
    }

    updateMilitaryPointState(id, normalizedRaw, player);
    militaryPlayerSnapshots.value = { ...militaryPlayerSnapshots.value, [id]: normalizedRaw };
};

const findPlayerById = (id: string): PlayerInfo | undefined => {
    return currentBluePlayers.value.find(player => getPlayerUniqueId(player) === id)
        || currentRedPlayers.value.find(player => getPlayerUniqueId(player) === id);
};

const LIKE_GIF_DISPLAY_MS = 1500;
const progressCache = ref<Record<string, number>>({});
const likeGifStates = ref<Record<string, { playing: boolean; token: number }>>({});
const likeGifHideTimers: Record<string, ReturnType<typeof setTimeout>> = {};
const likeGifUrl = likeGif;
const likeGifStaticSrc = ref<string>(likeGif);
const likeGifStaticLoaded = ref(false);

interface UpgradeTrackingEntry {
    announcedRank: number;
}

const upgradeTrackingMap = ref<Record<string, UpgradeTrackingEntry>>({});

const getPlayerLiveRank = (player: PlayerInfo): number => {
    const totalPoints = getPlayerTotalPoints(player);
    return getRank(totalPoints);
};

const getStepSize = (level: number): number => {
    if (level >= 100) return 1;
    if (level >= 80) return 2;
    return 5;
};

const getThresholdEffectLevel = (level: number): number => {
    if (level >= 96) return 96;
    if (level >= 80) return 80;
    if (level >= 64) return 64;
    if (level >= 48) return 48;
    if (level >= 32) return 32;
    if (level >= 16) return 16;
    return 1;
};

const chunkArray = <T>(array: T[], size: number): T[][] => {
    const result: T[][] = [];
    for (let i = 0; i < array.length; i += size) {
        result.push(array.slice(i, i + size));
    }
    return result;
};

const getPlayerPlatformId = (player: PlayerInfo): string | null => {
    const platformId = (player as any).userid_platform;
    if (!platformId) return null;
    return String(platformId);
};

const collectPlayerPlatformIds = (): string[] => {
    const ids = new Set<string>();
    combinedPlayers.value.forEach(player => {
        const id = getPlayerPlatformId(player);
        if (id) ids.add(id);
    });
    return Array.from(ids);
};

const findPlayerByPlatformId = (platformId: string): PlayerInfo | undefined => {
    if (!platformId) return undefined;
    return currentBluePlayers.value.find(player => String((player as any).userid_platform) === platformId)
        || currentRedPlayers.value.find(player => String((player as any).userid_platform) === platformId);
};

const applySeasonRankUpdates = (items: any[]) => {
    if (!Array.isArray(items)) {
        return;
    }
    items.forEach(item => {
        const platformId = item?.userid_platform;
        if (!platformId) return;
        const player = findPlayerByPlatformId(String(platformId));
        if (!player) return;
        if (Object.prototype.hasOwnProperty.call(item, 'rank')) {
            player.rank = item.rank ?? null;
        }
        if (Object.prototype.hasOwnProperty.call(item, 'week_rank')) {
            player.week_rank = item.week_rank ?? null;
        }
    });
};

const SEASON_POLL_INTERVAL = 60 * 1000;
let seasonPollTimer: number | null = null;
let seasonPollingInFlight = false;

const syncUpgradeTracking = (players: PlayerInfo[]) => {
    const nextMap: Record<string, UpgradeTrackingEntry> = {};
    for (const player of players) {
        const id = getPlayerUniqueId(player);
        if (!id) continue;
        const seasonPoints = Number(player.season_points) || 0;
        const baseRank = getRank(seasonPoints);
        const stepSize = getStepSize(baseRank);
        const bucket = Math.floor(baseRank / stepSize) * stepSize;
        const previous = upgradeTrackingMap.value[id];
        const announcedRank = previous ? Math.max(previous.announcedRank, bucket) : bucket;
        nextMap[id] = { announcedRank };
    }
    upgradeTrackingMap.value = nextMap;
};

const emitUpgradeEventIfNeeded = () => {
    if (!combinedPlayers.value.length) {
        return;
    }

    const triggeredPlayers: Array<{
        playerName: string;
        avatar: string;
        newLevel: number;
        oldLevel: number;
        rank?: number;
        playerId: string;
        effectLevel?: number;
        userid_platform?: string | number;
    }> = [];

    const trackingSnapshot = { ...upgradeTrackingMap.value };

    for (const player of combinedPlayers.value) {
        const id = getPlayerUniqueId(player);
        if (!id) continue;
        if ((player as any).seasonDataReady === null) {
            continue;
        }
        const tracking = trackingSnapshot[id];
        if (!tracking) continue;

        const baseRank = tracking.announcedRank;
        const liveRank = getPlayerLiveRank(player);
        const stepSize = getStepSize(baseRank);
        const baseBucket = Math.floor(baseRank / stepSize) * stepSize;
        const stepsPassed = Math.floor((liveRank - baseBucket) / stepSize);

        if (stepsPassed <= 0) {
            continue;
        }

        const newBase = baseBucket + stepsPassed * stepSize;

        trackingSnapshot[id] = {
            announcedRank: newBase
        };

        triggeredPlayers.push({
            playerName: player.player_name,
            avatar: player.avatar,
            newLevel: liveRank,
            oldLevel: baseRank,
            rank: player.rank,
            playerId: id,
            effectLevel: getThresholdEffectLevel(liveRank),
            userid_platform: (player as any).userid_platform ?? null
        });
    }

    upgradeTrackingMap.value = trackingSnapshot;

    if (triggeredPlayers.length) {
        eventBus.emit('upgrade', triggeredPlayers);
    }
};

const removeGifState = (id: string) => {
    if (likeGifHideTimers[id] !== undefined) {
        clearTimeout(likeGifHideTimers[id]);
        delete likeGifHideTimers[id];
    }
    if (likeGifStates.value[id]) {
        const { [id]: _, ...rest } = likeGifStates.value;
        likeGifStates.value = rest;
    }
};

const triggerLikeGif = (id: string) => {
    const token = Date.now();
    likeGifStates.value = { ...likeGifStates.value, [id]: { playing: true, token } };

    if (likeGifHideTimers[id] !== undefined) {
        clearTimeout(likeGifHideTimers[id]);
    }

    likeGifHideTimers[id] = setTimeout(() => {
        const state = likeGifStates.value[id];
        if (!state) {
            return;
        }
        likeGifStates.value = { ...likeGifStates.value, [id]: { ...state, playing: false } };
        delete likeGifHideTimers[id];
    }, LIKE_GIF_DISPLAY_MS);
};

const updateProgressTracking = (players: PlayerInfo[]) => {
    syncUpgradeTracking(players);
    const newCache = { ...progressCache.value };
    const seen = new Set<string>();

    for (const player of players) {
        const id = getPlayerUniqueId(player);
        if (!id) {
            continue;
        }
        syncMilitaryPointsFromPlayer(player);
        seen.add(id);

        const progressValue = Number((player as any).progress);
        if (!Number.isFinite(progressValue) || progressValue < 0) {
            if (newCache[id] !== undefined) {
                delete newCache[id];
                removeGifState(id);
            }
            continue;
        }

        const previous = progressCache.value[id];
        if (typeof previous === 'number' && progressValue > previous) {
            triggerLikeGif(id);
        }

        newCache[id] = progressValue;
    }

    for (const id of Object.keys(progressCache.value)) {
        if (!seen.has(id)) {
            delete newCache[id];
            removeGifState(id);
            clearMilitaryDiffState(id);
            if (militaryPointsMap.value[id] !== undefined) {
                const { [id]: _, ...rest } = militaryPointsMap.value;
                militaryPointsMap.value = rest;
            }
            if (militaryPlayerSnapshots.value[id] !== undefined) {
                const { [id]: _snapshot, ...restSnapshots } = militaryPlayerSnapshots.value;
                militaryPlayerSnapshots.value = restSnapshots;
            }
        }
    }

    progressCache.value = newCache;
};

const combinedPlayers = computed(() => [
    ...currentBluePlayers.value,
    ...currentRedPlayers.value
]);

watch(combinedPlayers, players => {
    updateProgressTracking(players);
}, { deep: true, immediate: true });


const getLikeGifSrc = (player: PlayerInfo): string => {
    const id = getPlayerUniqueId(player);
    const state = likeGifStates.value[id];
    if (state?.playing) {
        return `${likeGifUrl}?t=${state.token}`;
    }
    return likeGifStaticSrc.value || likeGifUrl;
};

const prepareLikeGifStatic = () => {
    if (likeGifStaticLoaded.value || typeof window === 'undefined') {
        return;
    }
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = likeGifUrl;
    img.onload = () => {
        try {
            const canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            const ctx = canvas.getContext('2d');
            if (ctx) {
                ctx.drawImage(img, 0, 0);
                const data = canvas.toDataURL('image/png');
                if (data) {
                    likeGifStaticSrc.value = data;
                }
            }
        } catch (_err) {
            // ignore extraction failure, fallback to animated gif
        } finally {
            likeGifStaticLoaded.value = true;
        }
    };
    img.onerror = () => {
        likeGifStaticLoaded.value = true;
    };
};

const currentRedHpCurrent = computed(() => isInternalDemo.value ? demoRedHpCurrent.value : props.redHpCurrent);
const currentBlueHpCurrent = computed(() => isInternalDemo.value ? demoBlueHpCurrent.value : props.blueHpCurrent);

const effectiveWinTeam = computed<string | null>(() => {
    if (currentRedHpCurrent.value > currentBlueHpCurrent.value) {
        return 'red';
    }
    if (currentBlueHpCurrent.value > currentRedHpCurrent.value) {
        return 'blue';
    }
    return null;
});

const settlementScoreMap = ref<Record<string, number>>({});
let settlementJobToken = 0;
let settlementScheduleHandle: number | null = null;

// 缓存结算积分计算结果，避免重复计算
const settlementScoresCache = ref<{
    key: string;
    scores: Record<string, number>;
}>({ key: '', scores: {} });

const executeSettlementCalculation = () => {
    if (!currentMessages.value?.length) {
        settlementScoreMap.value = {};
        settlementScoresCache.value = { key: '', scores: {} };
        return;
    }

    const messagesKey = currentMessages.value
        .map(m => `${m.player_id}-${m.team}-${m.gift_point}-${m.gift_count}`)
        .join('|');
    const cacheKey = `${messagesKey}-${currentRedHpCurrent.value}-${currentBlueHpCurrent.value}-${props.pointPool}`;

    if (settlementScoresCache.value.key === cacheKey) {
        settlementScoreMap.value = settlementScoresCache.value.scores;
        return;
    }

    const allPlayers: any[] = [];
    for (const player of currentBluePlayers.value) {
        allPlayers.push({ ...player, team: 'blue' });
    }
    for (const player of currentRedPlayers.value) {
        allPlayers.push({ ...player, team: 'red' });
    }

    const payloadMessages = currentMessages.value.map(message => ({ ...message }));
    const jobToken = ++settlementJobToken;

    calculatePlayerScoresAsync(
        allPlayers,
        payloadMessages,
        effectiveWinTeam.value,
        props.pointPool,
        '实时预估',
        false
    ).then(playersWithScores => {
        if (jobToken !== settlementJobToken) {
            return;
        }
        const scoreMap: Record<string, number> = {};
        for (const player of playersWithScores) {
            scoreMap[player.player_id] = player.finalScore;
        }
        settlementScoresCache.value = { key: cacheKey, scores: scoreMap };
        settlementScoreMap.value = scoreMap;
    }).catch(error => {
        if (jobToken !== settlementJobToken) {
            return;
        }
        console.error('[PlayerBox] 结算积分计算失败，使用同步回退:', error);
        const fallbackPlayers = calculatePlayerScores(
            allPlayers,
            payloadMessages,
            effectiveWinTeam.value,
            props.pointPool,
            '实时预估',
            false
        );
        const scoreMap: Record<string, number> = {};
        for (const player of fallbackPlayers) {
            scoreMap[player.player_id] = player.finalScore;
        }
        settlementScoresCache.value = { key: cacheKey, scores: scoreMap };
        settlementScoreMap.value = scoreMap;
    });
};

const scheduleSettlementCalculation = () => {
    if (typeof window === 'undefined') {
        executeSettlementCalculation();
        return;
    }
    if (settlementScheduleHandle !== null) {
        window.clearTimeout(settlementScheduleHandle);
    }
    settlementScheduleHandle = window.setTimeout(() => {
        settlementScheduleHandle = null;
        executeSettlementCalculation();
    }, 120);
};

watch(
    [
        currentMessages,
        currentBluePlayers,
        currentRedPlayers,
        currentRedHpCurrent,
        currentBlueHpCurrent,
        () => props.pointPool
    ],
    () => {
        scheduleSettlementCalculation();
    },
    { immediate: true }
);

// 计算当前积分的computed（缓存优化）
const currentScores = computed(() => {
    const result: Record<string, number> = {};
    
    // 如果有礼物消息，使用结算积分
    if (currentMessages.value?.length) {
        Object.assign(result, settlementScoreMap.value);
    } else {
        // 否则使用基础积分
        [...currentBluePlayers.value, ...currentRedPlayers.value].forEach(player => {
            const score = Number(player.score);
            if (!isNaN(score)) {
                result[player.player_id] = score;
            }
        });
    }
    
    // 同步到玩家对象的 score 字段，便于后续使用实时积分
    [...currentBluePlayers.value, ...currentRedPlayers.value].forEach(player => {
        const val = result[player.player_id];
        if (val !== undefined) {
            player.score = val;
        }
    });
    
    return result;
});

// 获取显示积分
const getScoreValue = (player: PlayerInfo): number => {
    return currentScores.value[player.player_id] || 0;
};

const getPlayerTotalPoints = (player: PlayerInfo): number => {
    const base = Number(player.season_points) || 0;
    return base + getScoreValue(player);
};

const fetchSeasonRanksBatch = async () => {
    if (seasonPollingInFlight) {
        return;
    }
    const ids = collectPlayerPlatformIds();
    if (!ids.length || !props.gameId) {
        return;
    }
    seasonPollingInFlight = true;
    const chunks = chunkArray(ids, 20);
    try {
        for (const chunk of chunks) {
            const res = await getApi.getPlayerSeasonData({
                game_id: props.gameId,
                userid_platforms: chunk.join(',')
            });
            const payload = res?.data?.data;
            const normalized = Array.isArray(payload) ? payload : (payload ? [payload] : []);
            applySeasonRankUpdates(normalized);
        }
    } catch (error) {
        console.error('[PlayerBox] 批量获取赛季数据失败:', error);
    } finally {
        seasonPollingInFlight = false;
    }
};

const startSeasonPolling = () => {
    if (seasonPollTimer !== null) {
        return;
    }
    if (!combinedPlayers.value.length || !props.gameId) {
        return;
    }
    void fetchSeasonRanksBatch();
    if (typeof window !== 'undefined') {
        seasonPollTimer = window.setInterval(() => {
            void fetchSeasonRanksBatch();
        }, SEASON_POLL_INTERVAL);
    }
};

const stopSeasonPolling = () => {
    if (seasonPollTimer !== null && typeof window !== 'undefined') {
        window.clearInterval(seasonPollTimer);
    }
    seasonPollTimer = null;
};

watch(
    () => ({
        playerCount: combinedPlayers.value.length,
        gameId: props.gameId
    }),
    ({ playerCount, gameId }) => {
        if (playerCount > 0 && gameId) {
            startSeasonPolling();
        } else {
            stopSeasonPolling();
        }
    },
    { immediate: true }
);

watch(
    () => ({
        scores: currentScores.value,
        players: combinedPlayers.value
    }),
    () => {
        emitUpgradeEventIfNeeded();
    },
    { deep: true }
);

// 演示模式切换
const toggleDemo = () => {
    isInternalDemo.value = !isInternalDemo.value;

    // 切换模式时重置滚动索引
    blueStartIndex.value = 0;
    redStartIndex.value = 0;
    
    // 同步滚动索引
    syncScrollIndexes();

    if (isInternalDemo.value) {
        demoRedHpCurrent.value = 800;
        demoBlueHpCurrent.value = 600;

        // 3秒后切换血量
        setTimeout(() => {
            if (isInternalDemo.value) {
                demoRedHpCurrent.value = 400;
                demoBlueHpCurrent.value = 900;
            }
        }, 3000);

        // 6秒后血量相等
        setTimeout(() => {
            if (isInternalDemo.value) {
                demoRedHpCurrent.value = 500;
                demoBlueHpCurrent.value = 500;
            }
        }, 6000);
    }
};


// 控制显示状态
const showControls = ref(false);

// 滚动相关
const blueStartIndex = ref(0);
const redStartIndex = ref(0);
const scrollTimer = ref<number | null>(null);
const MAX_VISIBLE_PLAYERS = 4;
const SCROLL_INTERVAL = 3000;
const PLAYER_CELL_HEIGHT = 68.8 + 20;
const PROGRESS_BAR_WIDTH = 200;
const BUFFER_SIZE = 1; // 缓冲区大小，在可见区域前后多渲染的元素数量

// 独立滚动状态管理
const blueScrollState = ref('scrolling'); // 'scrolling' | 'waiting'
const redScrollState = ref('scrolling'); // 'scrolling' | 'waiting'
const blueMaxScrollIndex = computed(() => {
    if (!blueNeedsScroll.value) return 0;
    return Math.max(0, currentBluePlayers.value.length - MAX_VISIBLE_PLAYERS);
});
const redMaxScrollIndex = computed(() => {
    if (!redNeedsScroll.value) return 0;
    return Math.max(0, currentRedPlayers.value.length - MAX_VISIBLE_PLAYERS);
});

// 列表变化处理
const isListChanging = ref(false);
const listChangeTimer = ref<number | null>(null);
const previousBlueCount = ref(0);
const previousRedCount = ref(0);

const blueNeedsScroll = computed(() => currentBluePlayers.value.length >= MAX_VISIBLE_PLAYERS);
const redNeedsScroll = computed(() => currentRedPlayers.value.length >= MAX_VISIBLE_PLAYERS);

// 计算可见范围内的玩家索引
const getVisibleRange = (startIndex: number, totalCount: number) => {
    const start = Math.max(0, startIndex - BUFFER_SIZE);
    const end = Math.min(totalCount, startIndex + MAX_VISIBLE_PLAYERS + BUFFER_SIZE);
    return { start, end };
};

// 虚拟滚动 - 蓝方玩家列表
const virtualBluePlayers = computed(() => {
    if (!blueNeedsScroll.value) {
        // 如果不需要滚动，显示所有玩家（始终从索引0开始）
        return currentBluePlayers.value.map((player, index) => ({
            ...player,
            virtualIndex: index,
            offsetTop: index * (PLAYER_CELL_HEIGHT * props.scale + 8)
        }));
    }
    
    // 只有在需要滚动时才使用滚动索引
    const safeStartIndex = Math.max(0, Math.min(blueStartIndex.value, currentBluePlayers.value.length - MAX_VISIBLE_PLAYERS));
    const { start, end } = getVisibleRange(safeStartIndex, currentBluePlayers.value.length);
    const virtualPlayers = [];
    
    for (let i = start; i < end; i++) {
        if (i >= 0 && i < currentBluePlayers.value.length) {
            virtualPlayers.push({
                ...currentBluePlayers.value[i],
                virtualIndex: i,
                offsetTop: i * (PLAYER_CELL_HEIGHT * props.scale + 8)
            });
        }
    }
    
    return virtualPlayers;
});

// 虚拟滚动 - 红方玩家列表
const virtualRedPlayers = computed(() => {
    if (!redNeedsScroll.value) {
        // 如果不需要滚动，显示所有玩家（始终从索引0开始）
        return currentRedPlayers.value.map((player, index) => ({
            ...player,
            virtualIndex: index,
            offsetTop: index * (PLAYER_CELL_HEIGHT * props.scale + 8)
        }));
    }
    
    // 只有在需要滚动时才使用滚动索引
    const safeStartIndex = Math.max(0, Math.min(redStartIndex.value, currentRedPlayers.value.length - MAX_VISIBLE_PLAYERS));
    const { start, end } = getVisibleRange(safeStartIndex, currentRedPlayers.value.length);
    const virtualPlayers = [];
    
    for (let i = start; i < end; i++) {
        if (i >= 0 && i < currentRedPlayers.value.length) {
            virtualPlayers.push({
                ...currentRedPlayers.value[i],
                virtualIndex: i,
                offsetTop: i * (PLAYER_CELL_HEIGHT * props.scale + 8)
            });
        }
    }
    
    return virtualPlayers;
});

// 计算总高度（用于保持滚动条正确）
const blueTotalHeight = computed(() => {
    return currentBluePlayers.value.length * (PLAYER_CELL_HEIGHT * props.scale + 8) - 8;
});

const redTotalHeight = computed(() => {
    return currentRedPlayers.value.length * (PLAYER_CELL_HEIGHT * props.scale + 8) - 8;
});

const blueScrollOffset = computed(() => {
    // 如果玩家数量少于最大可见数量，不需要滚动偏移
    if (!blueNeedsScroll.value) {
        return 0;
    }
    // 确保滚动索引不超过实际可滚动的范围
    const maxIndex = Math.max(0, currentBluePlayers.value.length - MAX_VISIBLE_PLAYERS);
    const actualIndex = Math.min(blueStartIndex.value, maxIndex);
    return actualIndex * (PLAYER_CELL_HEIGHT * props.scale + 8);
});

const redScrollOffset = computed(() => {
    // 如果玩家数量少于最大可见数量，不需要滚动偏移
    if (!redNeedsScroll.value) {
        return 0;
    }
    // 确保滚动索引不超过实际可滚动的范围
    const maxIndex = Math.max(0, currentRedPlayers.value.length - MAX_VISIBLE_PLAYERS);
    const actualIndex = Math.min(redStartIndex.value, maxIndex);
    return actualIndex * (PLAYER_CELL_HEIGHT * props.scale + 8);
});

// 智能同步滚动索引
const syncScrollIndexes = () => {
    // 重置滚动状态
    blueScrollState.value = 'scrolling';
    redScrollState.value = 'scrolling';
    
    // 只有当两边都需要滚动时才同步索引
    if (blueNeedsScroll.value && redNeedsScroll.value) {
        const syncIndex = Math.min(blueStartIndex.value, redStartIndex.value);
        blueStartIndex.value = syncIndex;
        redStartIndex.value = syncIndex;
    } else if (blueNeedsScroll.value && !redNeedsScroll.value) {
        // 只有蓝方需要滚动，红方保持在0
        redStartIndex.value = 0;
    } else if (!blueNeedsScroll.value && redNeedsScroll.value) {
        // 只有红方需要滚动，蓝方保持在0
        blueStartIndex.value = 0;
    } else {
        // 两边都不需要滚动，都重置为0
        blueStartIndex.value = 0;
        redStartIndex.value = 0;
    }
};

const scrollPlayers = () => {
    // 如果列表正在变化，跳过此次滚动
    if (isListChanging.value) {
        return;
    }
    
    // 处理蓝方滚动
    if (blueNeedsScroll.value && blueScrollState.value === 'scrolling') {
        if (blueStartIndex.value >= blueMaxScrollIndex.value) {
            // 蓝方滚动到底部，回溯到开头并进入等待状态
            blueStartIndex.value = 0;
            blueScrollState.value = 'waiting';
        } else {
            // 蓝方继续向下滚动
            blueStartIndex.value++;
        }
    }
    
    // 处理红方滚动
    if (redNeedsScroll.value && redScrollState.value === 'scrolling') {
        if (redStartIndex.value >= redMaxScrollIndex.value) {
            // 红方滚动到底部，回溯到开头并进入等待状态
            redStartIndex.value = 0;
            redScrollState.value = 'waiting';
        } else {
            // 红方继续向下滚动
            redStartIndex.value++;
        }
    }
    
    // 检查是否需要开始新一轮滚动
    if (blueScrollState.value === 'waiting' && redScrollState.value === 'waiting') {
        // 两边都完成了滚动，开始新一轮
        blueScrollState.value = 'scrolling';
        redScrollState.value = 'scrolling';
    } else if (blueScrollState.value === 'waiting' && !redNeedsScroll.value) {
        // 蓝方完成，红方不需要滚动，开始新一轮
        blueScrollState.value = 'scrolling';
    } else if (redScrollState.value === 'waiting' && !blueNeedsScroll.value) {
        // 红方完成，蓝方不需要滚动，开始新一轮
        redScrollState.value = 'scrolling';
    }
};

const startScrolling = () => {
    // 如果列表正在变化，延迟启动滚动
    if (isListChanging.value) {
        setTimeout(() => {
            if (!isListChanging.value && (blueNeedsScroll.value || redNeedsScroll.value)) {
                scrollTimer.value = window.setInterval(scrollPlayers, SCROLL_INTERVAL);
            }
        }, 500); // 500ms延迟
        return;
    }
    
    if (blueNeedsScroll.value || redNeedsScroll.value) {
        scrollTimer.value = window.setInterval(scrollPlayers, SCROLL_INTERVAL);
    }
};

const stopScrolling = () => {
    if (scrollTimer.value) {
        clearInterval(scrollTimer.value);
        scrollTimer.value = null;
    }
};

// 处理列表变化的防抖函数
const handleListChange = () => {
    isListChanging.value = true;
    
    // 暂停当前滚动
    stopScrolling();
    
    // 清除之前的定时器
    if (listChangeTimer.value) {
        clearTimeout(listChangeTimer.value);
    }
    
    // 设置新的定时器，延迟恢复滚动
    listChangeTimer.value = window.setTimeout(() => {
        isListChanging.value = false;
        
        // 调整滚动索引以适应新的列表长度
        adjustScrollIndexes();
        
        // 恢复滚动（再次检查是否需要滚动）
        nextTick(() => {
            if (blueNeedsScroll.value || redNeedsScroll.value) {
                startScrolling();
            }
        });
    }, 1000); // 1秒后恢复滚动
};

// 智能调整滚动索引
const adjustScrollIndexes = () => {
    const blueCount = currentBluePlayers.value.length;
    const redCount = currentRedPlayers.value.length;
        
    // 先重置不需要滚动的一边的索引和状态
    if (!blueNeedsScroll.value) {
        blueStartIndex.value = 0;
        blueScrollState.value = 'scrolling';
    }
    
    if (!redNeedsScroll.value) {
        redStartIndex.value = 0;
        redScrollState.value = 'scrolling';
    }
    
    // 计算需要滚动的一边的安全索引
    if (blueNeedsScroll.value) {
        const maxBlueIndex = Math.max(0, blueCount - MAX_VISIBLE_PLAYERS);
        if (blueStartIndex.value > maxBlueIndex) {
            blueStartIndex.value = maxBlueIndex;
        }
    }
    
    if (redNeedsScroll.value) {
        const maxRedIndex = Math.max(0, redCount - MAX_VISIBLE_PLAYERS);
        if (redStartIndex.value > maxRedIndex) {
            redStartIndex.value = maxRedIndex;
        }
    }
    
    // 最后再进行同步处理
    syncScrollIndexes();
    
    // 更新记录的数量
    previousBlueCount.value = blueCount;
    previousRedCount.value = redCount;  
};

const handleMessage = async (message: any) => {
    switch (message.type) {
        case 'upPointMilitary': {
            const { userid_platform, point } = message || {};
            const id = String(userid_platform ?? '');
            const val = Number(point);
            if (id && !isNaN(val)) {
                const playerRef = findPlayerById(id);
                updateMilitaryPointState(id, val, playerRef);
            }
            break;
        }}
}

// 样式计算
const avatarStyle = computed(() => {
    const s = props.scale;
    return { width: `${60 * s}px`, height: `${60 * s}px` };
});

const nameStyle = computed(() => {
    const s = props.scale;
    return {
        fontSize: `${16 * s}px`,
        lineHeight: `${16 * s}px`,
        marginBottom: `${4 * s}px`,
        maxWidth: '268px',
        height: `${16 * s}px`,
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
    };
});

const statStyle = computed(() => {
    const s = props.scale;
    return { fontSize: `${15 * s}px`, marginRight: `${15 * s}px` };
});

const progressBarWidth = computed(() => {
    const s = props.scale;
    return `${PROGRESS_BAR_WIDTH * s}px`;
});

const likeGifStyle = computed<CSSProperties>(() => {
    const size = 32 * props.scale;
    const offsetX = 12 * props.scale;
    const offsetY = 8 * props.scale;
    return {
        position: 'absolute',
        width: `${size}px`,
        height: `${size}px`,
        right: `${offsetX}px`,
        bottom: `${offsetY}px`,
        objectFit: 'contain',
        pointerEvents: 'none'
    } as CSSProperties;
});

const cellStyle = computed<CSSProperties>(() => {
    const s = props.scale;
    return {
        display: 'flex',
        alignItems: 'center',
        padding: `${4 * s}px`,
        borderRadius: `${3 * s}px`,
        boxSizing: 'border-box',
        minHeight: `${PLAYER_CELL_HEIGHT * s}px`
    } as CSSProperties;
});



// 生命周期
onMounted(() => {
    prepareLikeGifStatic();
    eventBus.on('socket-message', message => handleMessage(message));

    // 初始化时重置滚动索引，确保玩家框正确显示
    blueStartIndex.value = 0;
    redStartIndex.value = 0;
    
    // 初始化滚动状态
    blueScrollState.value = 'scrolling';
    redScrollState.value = 'scrolling';
    
    // 初始化玩家数量记录
    previousBlueCount.value = currentBluePlayers.value.length;
    previousRedCount.value = currentRedPlayers.value.length;
    
    // 同步滚动索引
    syncScrollIndexes();
    startScrolling();
});

onUnmounted(() => {
    stopScrolling();
    stopSeasonPolling();

    // 清理列表变化定时器
    if (listChangeTimer.value) {
        clearTimeout(listChangeTimer.value);
        listChangeTimer.value = null;
    }
    Object.keys(likeGifHideTimers).forEach(id => {
        clearTimeout(likeGifHideTimers[id]);
        delete likeGifHideTimers[id];
    });
    likeGifStates.value = {};
    Object.keys(militaryDiffTimers).forEach(id => {
        clearTimeout(militaryDiffTimers[id]);
        delete militaryDiffTimers[id];
    });
    militaryDiffStates.value = {};
    militaryPlayerSnapshots.value = {};
    progressCache.value = {};
    if (settlementScheduleHandle !== null && typeof window !== 'undefined') {
        window.clearTimeout(settlementScheduleHandle);
        settlementScheduleHandle = null;
    }
});

// 监听玩家数量变化，平滑处理滚动
watch([() => currentBluePlayers.value.length, () => currentRedPlayers.value.length], ([newBlueLength, newRedLength]) => {    
    // 检测是否有变化
    const hasBlueChange = newBlueLength !== previousBlueCount.value;
    const hasRedChange = newRedLength !== previousRedCount.value;
    
    if (hasBlueChange || hasRedChange) {
        handleListChange();
        return;
    }
    
    // 如果没有变化，正常处理滚动逻辑
    const shouldScroll = blueNeedsScroll.value || redNeedsScroll.value;

    if (shouldScroll && !scrollTimer.value && !isListChanging.value) {
        startScrolling();
    } else if (!shouldScroll && scrollTimer.value) {
        stopScrolling();
    }
}, { immediate: false });
</script>

<style scoped lang="less">
@import '@/assets/xinJi/css/PlayerBox.less';
</style>
